home *** CD-ROM | disk | FTP | other *** search
- /* ---------------------------------------------------------------------------------
- * PING.C
- *
- * $Locker:
- *
- * $Id:
- *
- * $Revision:
- *
- * $Header:
- *
- *-----------------------------------------------------------------------------------
- */
-
- /* sample internet ping client */
-
- /* This is an advanced sockets program that shows how to use */
- /* raw sockets, async I/O, and timers. This code will only */
- /* work with AmigaDOS 2.0. To run under 1.3, simply replace */
- /* ReadArgs() with your own parsing code and use another */
- /* timer in place of the ReadEClock() calls. */
-
- /* to compile with SAS C , type
- "lc -cfist -v -inetinc: -fi -Lm ping.c" */
-
- /* Written by Martin Hunt, Commodore-Amiga, June 1991 */
-
- /* Parts of this program were based on code by Mike Muuss */
-
- /* This code is placed in the public domain */
-
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <exec/tasks.h>
- #include <dos/dos.h>
- #include <devices/timer.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <string.h>
- #include <proto/all.h>
-
- /* shared library stuff */
- #include <ss/socket.h>
- #include <sys/ioctl.h>
- #include <sys/socket.h>
- #include <signal.h>
- #include <netinet/in_systm.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/ip_icmp.h>
- #include <netdb.h>
-
-
- /* get version and revision information */
- #include "ping_rev.h"
-
- /* This is used with the templates to cause the version string to be */
- /* stored in the command. It depends upon the command having a #include */
- /* of the appropriate version file to provide the value for this string */
- #define CMDREV "\0$VER: " VSTRING
-
- /* template for ReadArgs() */
- #define TEMPLATE "-L/S,HOST/A,SIZE/N,COUNT/N" CMDREV
- #define OPT_L 0
- #define OPT_HOST 1
- #define OPT_SIZE 2
- #define OPT_COUNT 3
- #define OPT_MAX 4
-
- #define MAXPACKET 4096 /* max packet size */
- struct Library *SockBase ;
- struct Library *TimerBase ;
- UBYTE buffer[MAXPACKET];
- struct MsgPort *timerport1=NULL;
- struct timerequest *timer1=NULL;
- u_long ntransmitted = 0, nreceived = 0;
- u_long tsum = 0;
- u_long tmin = 999999;
- u_long tmax = 0;
- u_long e_freq;
- UWORD ident;
- long size,count;
- BOOL loop = (BOOL)0;
- long return_code = RETURN_ERROR;
-
- /* prototypes */
- UWORD in_cksum(UWORD *addr, int len);
- ULONG create_timers(void);
- void send_icmp(int s, struct sockaddr_in *to);
- char *pr_type( int t );
- void read_icmp(int s );
- void pr_pack( int cc, struct sockaddr_in *from );
-
- void main (void)
- {
- char *host, *hostname;
- long timerevent, netevent, event;
- struct RDargs *rdargs;
- int s;
- struct sockaddr_in to;
- struct hostent *hp;
- struct protoent *proto;
- long true = 1;
- char *toaddr = NULL;
- long opts[OPT_MAX];
- UWORD abort = 0;
-
- memset((char *)opts, 0, sizeof(opts));
- rdargs = ReadArgs(TEMPLATE, opts, NULL);
-
- if ((SockBase = OpenLibrary( "inet:libs/socket.library", 1L ))==NULL) {
- printf("Opening of socket library failed.\n");
- goto exit1;
- }
-
- /* initialize for 5 sockets */
- setup_sockets( 5, &errno );
-
- /* process arguments */
- if(opts[OPT_COUNT])
- count = *(long *)opts[OPT_COUNT];
- else
- count = 100;
-
- if(opts[OPT_L])
- loop = (BOOL)opts[OPT_L];
- else
- count = 1;
-
- if (!(host = (char *)opts[OPT_HOST]))
- goto exit2;
-
- if(opts[OPT_SIZE])
- size = max( *(long *)opts[OPT_SIZE], 64);
- else
- size = 64;
-
- /* zero structure */
- memset( (char *)&to, '\0',sizeof(struct sockaddr_in) );
-
- to.sin_family = AF_INET;
- to.sin_addr.s_addr = inet_addr(host);
- if (to.sin_addr.s_addr == INADDR_NONE) {
- hp = gethostbyname(host);
- if (!hp) {
- printf("Error finding host %s\n",host);
- goto exit2;
- }
- to.sin_family = hp->h_addrtype;
- memcpy((char *)&to.sin_addr, hp->h_addr, hp->h_length);
- hostname = hp->h_name;
- toaddr = inet_ntoa(to.sin_addr.s_addr);
- } else
- hostname = host;
-
- /* ident is used in the ICMP packet so we can tell that its ours */
- ident = (UWORD)((long)FindTask(0L) & 0xFFFF);
-
- if ((proto = getprotobyname("icmp")) == NULL) {
- printf("icmp: unknown protocol\n");
- goto exit2;
- }
- /* open an ICMP socket */
- if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
- printf("ping: socket: %s\n",strerror(errno));
- goto exit2;
- }
-
- /* set socket to ASYNC so we will be signaled when packets arrive */
- if(s_ioctl(s, FIOASYNC, (char *)&true) < 0){
- printf("ping: s_ioctl: %s\n",strerror(errno));
- goto exit2;
- }
- /* set socket to nonblocking */
- if(s_ioctl(s, FIONBIO, (char *)&true) < 0){
- printf("ping: s_ioctl: %s\n",strerror(errno));
- goto exit2;
- }
-
- if(loop) {
- printf("PING %s", hostname);
- if (toaddr)
- printf(" (%s)", toaddr);
- printf(": %ld data bytes\n", size);
- }
-
- /* setup timer. timer1 is a 1 second timer for sending packets */
-
- if(create_timers()) {
- printf("Error setting up timer\n");
- goto exit2;
- }
-
- timerevent = 1L << timerport1->mp_SigBit;
- netevent = 1L << s_getsignal(SIGIO);
-
- do {
- /* send ICMP echo request */
- send_icmp(s,&to);
- for(;;) {
- /* wait for a response, timeout, or ^C */
- event = Wait(netevent | timerevent | SIGBREAKF_CTRL_C);
- if(event & netevent) {
- read_icmp(s);
- SetSignal(0L,netevent);
- if(!loop) {
- AbortIO((struct IORequest *)timer1);
- WaitIO((struct IORequest *)timer1);
- abort++;
- break;
- }
-
- }
- if(event & SIGBREAKF_CTRL_C) {
- AbortIO((struct IORequest *)timer1);
- WaitIO((struct IORequest *)timer1);
- abort++;
- break;
- }
- if(event & timerevent) {
- AbortIO((struct IORequest *)timer1);
- WaitIO((struct IORequest *)timer1);
- SetSignal(0L, timerevent);
- break;
- }
- }
-
- } while (!abort && (ntransmitted != count) );
-
- if(loop) {
- printf("\n----%s PING Statistics----\n", hostname );
- printf("%d packets transmitted, ", ntransmitted );
- printf("%d packets received, ", nreceived );
- if (ntransmitted)
- printf("%d%% packet loss",
- (int) (((ntransmitted-nreceived)*100) / ntransmitted ) );
- printf("\n");
- if (nreceived)
- printf("round-trip (ms) min/avg/max = %#2.2lf/%#2.2lf/%#2.2lf\n",
- (double)tmin/(double)e_freq*1000.0,
- (double)(tsum*1000) / (double)(nreceived*e_freq),
- (double)tmax/(double)e_freq*1000.0 );
- } else
- if(nreceived)
- printf("%s is alive!\n",host);
-
- fflush(stdout);
-
- if (s)
- s_close(s);
-
- if(timerport1)
- DeleteMsgPort(timerport1);
- if(timer1)
- DeleteIORequest(timer1);
- exit2:
- cleanup_sockets();
- CloseLibrary(SockBase);
- exit1:
- if(rdargs)
- FreeArgs(rdargs);
- CloseLibrary((struct Library *)DOSBase);
-
- exit(return_code);
- }
-
- /*
- * I N _ C K S U M
- *
- * Checksum routine for Internet Protocol family headers (C Version)
- *
- */
- UWORD in_cksum(UWORD *addr, int len)
- {
- register int nleft = len;
- register UWORD *w = addr;
- register UWORD answer;
- register int sum = 0;
- UWORD odd_byte = 0;
-
- /*
- * Our algorithm is simple, using a 32 bit accumulator (sum),
- * we add sequential 16 bit words to it, and at the end, fold
- * back all the carry bits from the top 16 bits into the lower
- * 16 bits.
- */
- while( nleft > 1 ) {
- sum += *w++;
- nleft -= 2;
- }
-
- /* mop up an odd byte, if necessary */
- if( nleft == 1 ) {
- *(u_char *)(&odd_byte) = *(u_char *)w;
- sum += odd_byte;
- }
-
- /*
- * add back carry outs from top 16 bits to low 16 bits
- */
- sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
- sum += (sum >> 16);
- answer = ~sum; /* truncate to 16 bits */
- return (answer);
- }
-
-
- ULONG create_timers(void)
- {
- timerport1 = CreateMsgPort();
- if(timerport1 == (struct MsgPort *)0)
- return (1);
-
- timer1 = (struct timerequest *)CreateIORequest(timerport1,(long)sizeof(struct timerequest));
- if(timer1 == (struct MsgPort *)0)
- return(1);
-
- if(OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)timer1, 0L) != 0L)
- return(1);
-
- /* set up TimerBase so we can use ReadEClock() */
- TimerBase = (struct Library *)timer1->tr_node.io_Device;
-
- return(0);
- }
-
- /* send an ICMP echo request */
- /* the timestamp is written in the first 8 bytes of data */
-
- void send_icmp(int s, struct sockaddr_in *to)
- {
- UBYTE *datap;
- struct icmp *icp = (struct icmp *) buffer;
- int i;
-
- icp->icmp_type = ICMP_ECHO;
- icp->icmp_code = 0;
- icp->icmp_cksum = 0;
- icp->icmp_seq = ntransmitted++;
- icp->icmp_id = ident;
-
- /* start the 1 second timer */
- timer1->tr_node.io_Command = TR_ADDREQUEST;
- timer1->tr_time.tv_secs = 1L;
- timer1->tr_time.tv_micro = 0L;
- SendIO((struct IORequest *)timer1);
-
-
- /* get system time and put it in first 8 bytes of data field */
- /* note that we will be timing filling the packet with data */
- /* and checksumming it. */
-
- ReadEClock((struct EClockVal *)(buffer+8));
-
- datap = (UBYTE *)(buffer+16); /* skip 8 for header and 8 for timestamp */
-
- /* fill packet with data */
- for( i=16; i<size; i++)
- *datap++ = i;
-
- /* Compute ICMP checksum */
- icp->icmp_cksum = in_cksum( (UWORD *)icp, size );
-
- /* send it */
- i = sendto( s, buffer, size, 0, to, sizeof(struct sockaddr) );
-
- if( i < 0 || i != size ) {
- if( i<0 )
- printf("sendto: %s\n",strerror(errno));
- printf("ping: wrote %ld chars, ret=%ld\n",size, i );
- }
-
- }
-
- void read_icmp(int s)
- {
- struct sockaddr_in from;
- int fromlen = sizeof (from);
- int cc;
-
- if ( (cc=recvfrom(s, buffer, MAXPACKET, 0, &from, &fromlen)) < 0) {
- if( errno == EINTR || errno == EWOULDBLOCK){
- return ;
- }
- printf("ping: recvfrom: %s\n",strerror(errno));
- return ;
- }
- pr_pack( cc, &from );
- }
-
-
- /*
- * P R _ P A C K
- *
- * Print out the packet, if it came from us. This logic is necessary
- * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
- * which arrive ('tis only fair). This permits multiple copies of this
- * program to be run without having intermingled output (or statistics!).
- */
- void pr_pack( int cc, struct sockaddr_in *from )
- {
- struct ip *ip;
- register struct icmp *icp;
- register long *lp = (long *) buffer;
- register int i;
- struct EClockVal intime, *outtime;
- int hlen;
- u_long triptime;
-
-
- e_freq = ReadEClock(&intime);
- from->sin_addr.s_addr = ntohl( from->sin_addr.s_addr );
- ip = (struct ip *)buffer;
-
- /* get the IP header length */
- hlen = ip->ip_hl << 2;
-
- if (cc < hlen + ICMP_MINLEN) {
- printf("packet too short (%d bytes) from %s\n", cc,
- inet_ntoa(ntohl(from->sin_addr.s_addr)));
- return;
- }
- cc -= hlen;
-
- /* the ICMP packet starts after the IP header */
- icp = (struct icmp *)(buffer + hlen);
- if( icp->icmp_type != ICMP_ECHOREPLY ) {
- printf("%d bytes from %s: ", cc,
- inet_ntoa(ntohl(from->sin_addr.s_addr)));
- printf("icmp_type=%d (%s)\n",
- icp->icmp_type, pr_type(icp->icmp_type) );
- for( i=0; i<12; i++)
- printf("x%2.2x: x%8.8x\n", i*sizeof(long), *lp++ );
- printf("icmp_code=%d\n", icp->icmp_code );
- return;
- }
- if( icp->icmp_id != ident )
- return; /* 'Twas not our ECHO */
-
- return_code = RETURN_OK;
-
- outtime = (struct EClockVal *)(&icp->icmp_data[0]);
- triptime = intime.ev_lo-outtime->ev_lo;
-
- if(loop) {
- printf("%d bytes from %s: ", cc,inet_ntoa(ntohl(from->sin_addr.s_addr)));
- printf("icmp_seq=%d. ", icp->icmp_seq );
- printf("time=%#2.2lf ms\n", (double)triptime/(double)e_freq*1000.0 );
- }
-
- /* update total, max, and min times */
- tsum += triptime;
- tmax = max(tmax,triptime);
- tmin = min(tmin,triptime);
-
- nreceived++;
- }
-
- /*
- * P R _ T Y P E
- *
- * Convert an ICMP "type" field to a printable string.
- */
- char *pr_type( int t )
- {
- static char *ttab[] = {
- "Echo Reply",
- "ICMP 1",
- "ICMP 2",
- "Dest Unreachable",
- "Source Quench",
- "Redirect",
- "ICMP 6",
- "ICMP 7",
- "Echo",
- "ICMP 9",
- "ICMP 10",
- "Time Exceeded",
- "Parameter Problem",
- "Timestamp",
- "Timestamp Reply",
- "Info Request",
- "Info Reply"
- };
-
- if( t < 0 || t > 16 )
- return("OUT-OF-RANGE");
-
- return(ttab[t]);
- }
-